{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 面向对象编程\n",
    "\n",
    "Object Oriented Programming，简称OOP，是一种程序设计思想。OOP把对象作为程序的基本单元，一个对象包含了数据和操作数据的函数。\n",
    "\n",
    "面向过程的程序设计把计算机程序视为一系列的命令集合，即一组函数的顺序执行。为了简化程序设计，面向过程把函数继续切分为子函数，即把大块函数通过切割成小块函数来降低系统的复杂度。\n",
    "\n",
    "而面向对象的程序设计把计算机程序视为一组对象的集合，而每个对象都可以接收其他对象发过来的消息，并处理这些消息，计算机程序的执行就是一系列消息在各个对象之间传递。\n",
    "\n",
    "在Python中，所有数据类型都可以视为对象，当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类（Class）的概念。\n",
    "\n",
    "回顾一下我们已经学了的东西，都是什么类"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "my_list的类 <class 'list'>\n",
      "number的类 <class 'int'>\n",
      "str1的类 <class 'str'>\n",
      "dict1的类 <class 'dict'>\n"
     ]
    }
   ],
   "source": [
    "my_list = [1,2,3]\n",
    "print(\"my_list的类\",type(my_list))\n",
    "number = 40\n",
    "print(\"number的类\",type(number))\n",
    "str1 = 'QQ40'\n",
    "print(\"str1的类\",type(str1))\n",
    "dict1 = {'a':1}\n",
    "print(\"dict1的类\",type(dict1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_list.append(32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "40"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "number.numerator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 面向对象设计\n",
    "\n",
    "我们以一个例子来说明<strong>面向过程</strong>和<strong>面向对象</strong>在程序设计流程上的不同之处。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "卡罗拉: 12万元\n"
     ]
    }
   ],
   "source": [
    "# 假设我们要处理车的交易，为了表示一个车的属性，面向过程的程序可以用一个dict表示：\n",
    "car1 = {'price':12, 'name':'卡罗拉',\"owner\":\"小李\"}\n",
    "car2 = {'price':40, 'name':'皇冠',\"owner\":\"奥克\"}\n",
    "\n",
    "def print_info(std):\n",
    "    print('%s: %s万元' % (std['name'],std['price']))\n",
    "\n",
    "print_info(car1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果采用面向对象的程序设计思想，我们首选思考的不是程序的执行流程，而是Car这种数据类型应该被视为一个对象，这个对象拥有属性（Property）。如果要打印一辆车的信息，首先必须创建出这个车对应的对象，然后，给对象发一个print_info消息，让对象自己把自己的数据打印出来。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car(object): # object是最基础的类\n",
    "\n",
    "    def __init__(self, name, price,owner):#放在class里面的函数叫方法\n",
    "        # 定义这个类的属性\n",
    "        self.price = price\n",
    "        self.name = name\n",
    "        self.owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        # 要定义一个方法，除了第一个参数是self外，其他和普通函数一样。\n",
    "        # 要调用一个方法，只需要在实例变量上直接调用，除了self不用传递，其他参数正常传入：\n",
    "        print('{}的{}: {}万元'.format(self.owner, self.name, self.price))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "给对象发消息实际上就是调用对象对应的关联函数，我们称之为对象的方法（Method）。面向对象的程序写出来就像这样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小李的卡罗拉: 12万元\n"
     ]
    }
   ],
   "source": [
    "c1 = Car('卡罗拉', 12, '小李')\n",
    "c1.print_info()   # 这些数据和逻辑被“封装”起来了，调用很容易，但却不用知道内部实现的细节。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面向对象的设计思想是从自然界中来的，因为在自然界中，类（Class）和实例（Instance）的概念是很自然的。Class是一种抽象概念，比如我们定义的Class——Car，是指车这个概念，而实例（Instance）则是一个个具体的车，比如，Luc的车"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "class后面紧接着是类名，即Car，类名通常是大写开头的单词，紧接着是(object)，表示该类是从哪个类继承下来的，继承的概念我们后面再讲，通常，如果没有合适的继承类，就使用object类，这是所有类最终都会继承的类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建一个实例\n",
    "my_car = Car('五菱宏光', 8, '刘毅')  #my_car是变量，自己定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Car object at 0x0000000004EE0A90>\n"
     ]
    }
   ],
   "source": [
    "print(my_car)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以看到，变量my_car指向的就是一个Car的实例，后面的0x7f2d08585b70是内存地址，每个object的地址都不一样"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "刘毅的宝马: 8万元\n",
      "刘毅的宝马: -2万元\n"
     ]
    }
   ],
   "source": [
    "# 自由为对象赋值\n",
    "my_car.print_info() # 修改前\n",
    "my_car.name = \"宝马\"\n",
    "my_car.price = -2\n",
    "my_car.print_info() # 修改后"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类可以起到模板的作用，因此，可以在创建实例的时候，把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法，在创建实例的时候，就把name, price等属性绑上去：\n",
    "\n",
    "<strong>特殊方法“__init__”前后分别有两个下划线！！！</strong>\n",
    "\n",
    "注意到__init__方法的第一个参数永远是self，表示创建的实例本身，因此，在__init__方法内部，就可以把各种属性绑定到self，因为self就指向创建的实例本身。\n",
    "\n",
    "有了__init__方法，在创建实例的时候，就不能传入空的参数了，必须传入与__init__方法匹配的参数，但self不需要传，Python解释器自己会把实例变量传进去："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "__init__() missing 1 required positional argument: 'owner'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-21-91191b41c12b>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      1\u001b[0m \u001b[1;31m# 创建实例 缺失参数会报错\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 2\u001b[1;33m \u001b[0mmy_car\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mCar\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'五菱宏光'\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;36m8\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: __init__() missing 1 required positional argument: 'owner'"
     ]
    }
   ],
   "source": [
    "# 创建实例 缺失参数会报错\n",
    "my_car = Car('五菱宏光', 8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习1 \n",
    "\n",
    "写一个学生类型，包含姓名(name),年龄(age),专业(profession)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "小都年龄是18岁，专业是计算机系统\n"
     ]
    }
   ],
   "source": [
    "class Student(object):\n",
    "\n",
    "    def __init__(self, name, age, profession):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        self.profession = profession\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s年龄是%s岁，专业是%s' % (self.name, self.age, self.profession))\n",
    "\n",
    "s = Student('小都',18,'计算机系统')\n",
    "# 输出学生信息\n",
    "s.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "### 访问限制\n",
    "从前面Car类的定义来看，外部代码还是可以自由地修改一个实例的name、price等属性\n",
    "\n",
    "如果要让内部属性不被外部访问，可以把属性的名称前加上两个下划线__，\n",
    "\n",
    "在Python中，实例的变量名如果以__开头，就变成了一个私有变量（private），\n",
    "\n",
    "只有内部可以访问，外部不能访问，所以，我们把Car类改一改："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_2(object):\n",
    "\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (self.__owner, self.__name, self.__price))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "直接获取 本田\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Car_2' object has no attribute '__name'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-27-6b570ec1327f>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"直接获取\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmy_car1\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mname\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 5\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"报错\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmy_car2\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m__name\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;31m# 'Car_2' object has no attribute '__name'\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      6\u001b[0m \u001b[1;31m# 这样就确保了外部代码不能随意修改对象内部的状态，\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      7\u001b[0m \u001b[1;31m# 这样通过访问限制的保护，代码更加健壮。\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mAttributeError\u001b[0m: 'Car_2' object has no attribute '__name'"
     ]
    }
   ],
   "source": [
    "my_car1 = Car('本田', 12, '刘毅')\n",
    "my_car2 = Car_2('五菱宏光', 8, '初言')\n",
    "\n",
    "print(\"直接获取\", my_car1.name)\n",
    "print(\"报错\", my_car2.__name) # 'Car_2' object has no attribute '__name'\n",
    "# 这样就确保了外部代码不能随意修改对象内部的状态，\n",
    "# 这样通过访问限制的保护，代码更加健壮。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初言的五菱宏光: 8万元\n"
     ]
    }
   ],
   "source": [
    "my_car2.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1. 如果外部代码要获取name,price怎么办？可以给Car类增加get_name和get_price这样的方法\n",
    "2. 如果又要允许外部代码修改score怎么办？可以再给Student类增加set_score方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_3(object):\n",
    "\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (self.owner, self.name, self.price))\n",
    "    \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "\n",
    "    def get_price(self):\n",
    "        return self.__price\n",
    "    \n",
    "    def set_price(self, price):\n",
    "        self.__price = price"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "你也许会问，原先那种直接通过my_car.price = 20也可以修改啊，为什么要定义一个方法大费周折？因为在方法中，可以对参数做检查，避免传入无效的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_4(object):\n",
    "\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (\n",
    "            self.__owner, self.__name, self.__price))\n",
    "    \n",
    "    def get_name(self):\n",
    "        return self.__name\n",
    "\n",
    "    def get_price(self):\n",
    "        return self.__price\n",
    "    \n",
    "    def set_price(self, price):\n",
    "        if 2 <= price:\n",
    "            self.__price = price\n",
    "        else:\n",
    "            print('设置无效：价格不能为负数')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "设置无效：价格不能为负数\n"
     ]
    }
   ],
   "source": [
    "my_car4 = Car_4('五菱宏光', 8, 'Louis')\n",
    "my_car4.set_price(1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Louis的五菱宏光: 12万元\n"
     ]
    }
   ],
   "source": [
    "my_car4.set_price(12)\n",
    "my_car4.print_info()"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    "注意\n",
    "\n",
    "在Python中，变量名类似__xxx__的，也就是以双下划线开头，并且以双下划线结尾的，是特殊变量，特殊变量是可以直接访问的，不是private变量，所以，不能用__name__、__score__这样的变量名。\n",
    "\n",
    "有些时候，你会看到以一个下划线开头的实例变量名，比如_name，这样的实例变量外部是可以访问的，但是，按照约定俗成的规定，当你看到这样的变量时，意思就是，“虽然我可以被访问，但是，请把我视为私有变量，不要随意访问”。\n",
    "\n",
    "双下划线开头的实例变量是不是一定不能从外部访问呢？其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Car__name，所以，仍然可以通过_Car__name来访问__name变量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Louis的路虎: 8万元\n"
     ]
    }
   ],
   "source": [
    "my_car4._Car_4__name = \"路虎\"\n",
    "my_car4.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "但是强烈建议你不要这么干，因为不同版本的Python解释器可能会把__name改成不同的变量名。\n",
    "\n",
    "总的来说就是，Python本身没有任何机制阻止你干坏事，一切全靠自觉。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习2\n",
    "\n",
    "在上面的Car_4对象基础上添加　《颜色:color》　属性， 用get_color()获取颜色，set_color设置颜色"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car_5(Car_4):\n",
    "    # Car_4 代替 object，代表Car_5继承Car_4特征\n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "        self.__color = None\n",
    "\n",
    "    def print_info(self):\n",
    "        print('%s的%s%s: %s万元' % (\n",
    "            self.__owner, self.__color, self.__name, self.__price))\n",
    "    \n",
    "    # 添加颜色参数\n",
    "    def get_color(self):\n",
    "        return self.__color\n",
    "    \n",
    "    def set_color(self, color):\n",
    "        self.__color = color"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "未添加颜色: None\n",
      "添加颜色后: 红色\n",
      "小法的红色雷克萨斯: 18万元\n"
     ]
    }
   ],
   "source": [
    "my_car5 = Car_5('雷克萨斯', 18, '小法')\n",
    "print('未添加颜色:', my_car5.get_color())\n",
    "my_car5.set_color('红色')\n",
    "print('添加颜色后:', my_car5.get_color())\n",
    "my_car5.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 继承与多态\n",
    "\n",
    "在OOP程序设计中，当我们定义一个class的时候，可以从某个现有的class继承，新的class称为子类（Subclass），而被继承的class称为基类、父类或超类（Base class、Super class）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Animal(object):\n",
    "    def run(self):\n",
    "        print('Animal is running...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 当我们需要编写Dog和Cat类时，就可以直接从Animal类继承：\n",
    "class Dog(Animal):\n",
    "    pass\n",
    "\n",
    "class Cat(Animal):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对于Dog来说，Animal就是它的父类，对于Animal来说，Dog就是它的子类。Cat和Dog类似。\n",
    "\n",
    "继承有什么好处？最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法，因此，Dog和Cat作为它的子类，什么事也没干，就自动拥有了run()方法："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Animal is running...\n",
      "Animal is running...\n"
     ]
    }
   ],
   "source": [
    "dog = Dog()\n",
    "dog.run()\n",
    "\n",
    "cat = Cat()\n",
    "cat.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "继承的第二个好处需要我们对代码做一点改进。\n",
    "\n",
    "你看到了，无论是Dog还是Cat，它们run()的时候，显示的都是Animal is running...，\n",
    "\n",
    "符合逻辑的做法是分别显示Dog is running...和Cat is running...，\n",
    "\n",
    "因此，对Dog和Cat类改进如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Dog(Animal):\n",
    "\n",
    "    def run(self):\n",
    "        print('Dog is running...')\n",
    "\n",
    "class Cat(Animal):\n",
    "\n",
    "    def run(self):\n",
    "        print('Cat is running...')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running...\n",
      "Cat is running...\n"
     ]
    }
   ],
   "source": [
    "dog = Dog()\n",
    "dog.run()\n",
    "\n",
    "cat = Cat()\n",
    "cat.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当子类和父类都存在相同的run()方法时，我们说，子类的run()覆盖了父类的run()，在代码运行的时候，总是会调用子类的run()。这样，我们就获得了继承的另一个好处：多态\n",
    "\n",
    "### 多态\n",
    "\n",
    "要理解什么是多态，我们首先要对数据类型再作一点说明。当我们定义一个class的时候，我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型，比如str、list、dict没什么两样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "list类型 <class 'list'> True\n",
      "Animal类型 <class '__main__.Animal'> True\n",
      "Dog类型 <class '__main__.Dog'> True\n"
     ]
    }
   ],
   "source": [
    "a = list() # a是list类型\n",
    "b = Animal() # b是Animal类型\n",
    "c = Dog() # c是Dog类型\n",
    "\n",
    "#　判断一个变量是否是某个类型可以用isinstance()判断：\n",
    "print(\"list类型\", type(a), isinstance(a, list))\n",
    "print(\"Animal类型\", type(b), isinstance(b, Animal))\n",
    "print(\"Dog类型\", type(c), isinstance(c, Dog))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 尝试一下这个，Ｄｏｇ　是否也是　Animal　类型\n",
    "isinstance(c, Animal)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "看来c不仅仅是Dog，c还是Animal！\n",
    "\n",
    "不过仔细想想，这是有道理的，因为Dog是从Animal继承下来的，当我们创建了一个Dog的实例c时，我们认为c的数据类型是Dog没错，但c同时也是Animal也没错，Dog本来就是Animal的一种！\n",
    "\n",
    "所以，在继承关系中，如果一个实例的数据类型是某个子类，那它的数据类型也可以被看做是父类。但是，反过来就不行："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Animal 不是　Ｄｏｇ　类型\n",
    "isinstance(b,Dog)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "继承还可以一级一级地继承下来，就好比从爷爷到爸爸、再到儿子这样的关系。而任何类，最终都可以追溯到根类object，这些继承关系看上去就像一颗倒着的树。比如如下的继承树：\n",
    "\n",
    "object\n",
    "\n",
    "   |-->Animal\n",
    "  \n",
    "        |-->Dog\n",
    "        \n",
    "        |-->Cat\n",
    "        \n",
    "   |-->Plant\n",
    "  \n",
    "        |-->Tree\n",
    "        \n",
    "        |-->Flower\n",
    "       "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 静态语言 vs 动态语言\n",
    "\n",
    "对于静态语言（例如Java）来说，如果需要传入Animal类型，则传入的对象必须是Animal类型或者它的子类，否则，将无法调用run()方法。\n",
    "\n",
    "对于Python这样的动态语言来说，则不一定需要传入Animal类型。我们只需要保证传入的对象有一个run()方法就可以了："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Car(object):\n",
    "    def run(self):\n",
    "        print('Car run...')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这就是动态语言的“鸭子类型”，它并不要求严格的继承体系，一个对象只要“看起来像鸭子，走起路来像鸭子”，那它就可以被看做是鸭子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "def run_twice(animal):\n",
    "    animal.run()\n",
    "    animal.run()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dog is running...\n",
      "Dog is running...\n",
      "Cat is running...\n",
      "Cat is running...\n",
      "Car run...\n",
      "Car run...\n"
     ]
    }
   ],
   "source": [
    "run_twice(Dog())\n",
    "run_twice(Cat())\n",
    "run_twice(Car())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习3\n",
    "\n",
    "设计一个马(horse),它继承animal类，改写run方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Horse run...\n"
     ]
    }
   ],
   "source": [
    "# 练习：设计一个马(horse),它继承animal类，改写run方法\n",
    "\n",
    "class Horse(Animal):\n",
    "    \n",
    "    def run(self):\n",
    "        print('Horse run...')\n",
    "\n",
    "# 创建实例\n",
    "\n",
    "h = Horse()\n",
    "\n",
    "h.run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 扩展知识\n",
    "\n",
    "### __slots__\n",
    "要限制实例的属性\n",
    "\n",
    "比如，只允许对Student实例添加name和age属性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Tom\n"
     ]
    }
   ],
   "source": [
    "# 正常情况下，当我们定义了一个class，创建了一个class的实例后，\n",
    "# 我们可以给该实例绑定任何属性和方法，这就是动态语言的灵活性。先定义class\n",
    "s = Student()\n",
    "s.name = 'Tom'\n",
    "print(s.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 为了达到限制的目的，Python允许在定义class的时候，\n",
    "# 定义一个特殊的__slots__变量，来限制该class实例能添加的属性：\n",
    "\n",
    "class Student_2(object):\n",
    "    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Student_2' object has no attribute 'score'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-61-74b7accd7276>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mStudent_2\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscore\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m99\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m      3\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mscore\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Student_2' object has no attribute 'score'"
     ]
    }
   ],
   "source": [
    "s = Student_2()\n",
    "s.score = 99\n",
    "print(s.score)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 装饰器（decorator）\n",
    "\n",
    "python装饰器是Python中比较难理解的概念。简单来说装饰器是用来对某个函数增加功能的，但又不需要改变函数提的内容,看例子"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "执行myDectoation函数2\n",
      "执行myDectoation函数1\n",
      "正在执行内部函数1\n",
      "正在执行内部函数2\n",
      "------结果-------\n",
      "佛山欢迎您 广东欢迎你 中国欢迎你\n"
     ]
    }
   ],
   "source": [
    "def myDectoration1(func1):\n",
    "    print(\"执行myDectoation函数1\")\n",
    "    def inner1(*args):\n",
    "        print(\"正在执行内部函数1\")\n",
    "        return func1(*args)+\" 中国欢迎你\"\n",
    "    return inner1\n",
    "\n",
    "def myDectoration2(func2):\n",
    "    print(\"执行myDectoation函数2\")\n",
    "    def inner2(*args):\n",
    "        print(\"正在执行内部函数2\")\n",
    "        return func2(*args)+\" 广东欢迎你\"\n",
    "    return inner2\n",
    "\n",
    "\n",
    "@myDectoration1\n",
    "@myDectoration2\n",
    "def printMessage(name):\n",
    "    return \"%s欢迎您\"%name\n",
    "\n",
    "messgae = printMessage(\"佛山\")\n",
    "print(\"------结果-------\")\n",
    "print(messgae)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由于函数也是一个对象，而且函数对象可以被赋值给变量，所以，通过变量也能调用该函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student(object):\n",
    "\n",
    "    @property\n",
    "    def score(self):\n",
    "        return self._score\n",
    "\n",
    "    @score.setter\n",
    "    def score(self, value):\n",
    "        if not isinstance(value, int):\n",
    "            raise ValueError('score must be an integer!')\n",
    "        if value < 0 or value > 100:\n",
    "            print(\"score must between 0 ~ 100!\")\n",
    "            #raise ValueError('score must between 0 ~ 100!')\n",
    "            return\n",
    "        self._score = value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "神奇的@property，我们在对实例属性操作的时候，就知道该属性很可能不是直接暴露的，而是通过getter和setter方法来实现的。\n",
    "\n",
    "还可以定义只读属性，只定义getter方法，不定义setter方法就是一个只读属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "60\n",
      "score must between 0 ~ 100!\n",
      "60\n"
     ]
    }
   ],
   "source": [
    "s = Student()\n",
    "s.score = 60\n",
    "print(s.score)\n",
    "s.score = 999\n",
    "print(s.score)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {},
   "outputs": [],
   "source": [
    "## 只定义getter方法，不定义setter方法就是一个只读属性：\n",
    "class Student(object):\n",
    "\n",
    "    @property\n",
    "    def birth(self):\n",
    "        return self._birth\n",
    "\n",
    "    @birth.setter\n",
    "    def birth(self, value):\n",
    "        self._birth = value\n",
    "\n",
    "    @property\n",
    "    def age(self):\n",
    "        return 2018 - self._birth"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25\n"
     ]
    }
   ],
   "source": [
    "s = Student()\n",
    "#s.age = 10\n",
    "s.birth = 1993\n",
    "print(s.age)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的birth是可读写属性，而age就是一个只读属性，因为age可以根据birth和当前时间计算出来。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习4\n",
    "\n",
    "Ｃａｒ 对象中加上重量(weight)，出厂日期(created)两个私有属性，"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 练习－　　Ｃａｒ 对象中加上重量(weight)，出厂日期(created)两个私有属性，\n",
    "class Car_exec(object):\n",
    "    \n",
    "    def __init__(self, name, price,owner):\n",
    "        self.__price = price\n",
    "        self.__name = name\n",
    "        self.__owner = owner\n",
    "    \n",
    "    @property\n",
    "    def weight(self):\n",
    "        return self._weight\n",
    "    \n",
    "    @weight.setter\n",
    "    def weight(self, value):\n",
    "        self._weight = value\n",
    "    \n",
    "    @property\n",
    "    def created(self):\n",
    "        return self._created\n",
    "    \n",
    "    @created.setter\n",
    "    def created(self, value):\n",
    "        self._created = value\n",
    "    \n",
    "    def print_info(self):\n",
    "        print('%s的%s: %s万元' % (self.owner, self.name, self.price))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "2016\n"
     ]
    }
   ],
   "source": [
    "my_car = Car_exec('雷克萨斯', 18, '小法')\n",
    "my_car.weight = 10\n",
    "print(my_car.weight)\n",
    "my_car.created = 2016\n",
    "print(my_car.created)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
